package cn.snmp.mutual.nms.util;

import lombok.extern.slf4j.Slf4j;
import cn.snmp.mutual.uk.stack.AsnObject;
import cn.snmp.mutual.uk.stack.SnmpContextBasisFace;
import java.util.*;

@Slf4j
public class SnmpContextPool {

    protected static SnmpContextPool THIS;
    protected Hashtable<String, SnmpContextBasisFace> contexts;
    protected Hashtable<SnmpContextBasisFace, Long> lastUsed;
    protected Timer timer;
    protected long pollTimeMillis;
    protected long keepTimeMillis;

    public SnmpContextPool() {
        this.contexts = new Hashtable<String, SnmpContextBasisFace>();
        this.lastUsed = new Hashtable<SnmpContextBasisFace, Long>();
        this.timer = new Timer("snmpm_snmpcontextpool_releasesnmpcontext_timer");
        this.pollTimeMillis = 3000L;
        this.keepTimeMillis = 12000L;
        this.setupTimer();
    }

    public static synchronized SnmpContextPool getInstance() {
        if (SnmpContextPool.THIS == null) {
            SnmpContextPool.THIS = new SnmpContextPool();
        }
        return SnmpContextPool.THIS;
    }

    public void setPollInterval(final long pollTimeMillis) {
        this.pollTimeMillis = pollTimeMillis;
        this.setupTimer();
    }

    public void setKeepTimeMillis(final long keepTimeMillis) {
        this.keepTimeMillis = keepTimeMillis;
    }

    protected void releaseSnmpContext(final String key) {
        try {
            if (AsnObject.debug > 6) {
                log.debug(("release Snmp Session: " + key + "."));
            }
            final SnmpContextBasisFace context = this.contexts.get(key);
            this.contexts.remove(key);
            this.lastUsed.remove(context);
            context.destroy();
        } catch (Exception e) {
            this.log.debug("" + e);
        }
    }

    protected void setupTimer() {
        this.timer.cancel();
        this.timer = new Timer("snmpm_snmpcontextpool_releasesnmpcontext_timer");
        final TimerTask task = new TimerTask() {
            @Override
            public void run() {
                try {
                    if (System.currentTimeMillis() - this.scheduledExecutionTime() >= SnmpContextPool.this.pollTimeMillis) {
                        return;
                    }
                    if (AsnObject.debug > 6) {
                        log.debug(("SnmpContextPool: context count : [" + SnmpContextPool.this.contexts.size() + "]."));
                    }
                    final Enumeration<String> e = SnmpContextPool.this.contexts.keys();
                    while (e.hasMoreElements()) {
                        final String key = e.nextElement();
                        final SnmpContextBasisFace context = SnmpContextPool.this.contexts.get(key);
                        final long lused = SnmpContextPool.this.lastUsed.get(context);
                        final long keep = System.currentTimeMillis() - lused;
                        if (keep > SnmpContextPool.this.keepTimeMillis) {
                            SnmpContextPool.this.releaseSnmpContext(key);
                        }
                    }
                } catch (Exception e2) {
                    log.error(("SNMP context pool keeper crash,system will down..." + e2));
                }
            }
        };
        this.timer.scheduleAtFixedRate(task, 0L, this.pollTimeMillis);
    }

    protected String getCacheKey(final int version, final String hostname, final int port) {
        final String key = "[" + version + ":" + hostname + ":" + port + "]";
        return key;
    }

    public synchronized SnmpContextBasisFace getSnmpContext(final int version, final String hostname, final int port) {
        SnmpContextBasisFace context = null;
        try {
            if (!SnmpTarget.isSessionCtrl) {
                context = SnmpTarget.newSnmpContext(version, hostname, port);
                return context;
            }
            final String key = this.getCacheKey(version, hostname, port);
            context = this.contexts.get(key);
            if (context != null && !context.isDestroyed()) {
                this.lastUsed.put(context, System.currentTimeMillis());
            } else {
                context = SnmpTarget.newSnmpContext(version, hostname, port);
                this.contexts.put(key, context);
                this.lastUsed.put(context, System.currentTimeMillis());
            }
        } catch (Exception e) {
            log.debug("" + e);
        }
        return context;
    }

    static {
        SnmpContextPool.THIS = null;
    }
}
